﻿using System.Diagnostics;
using ContaPlanner.Data;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;

namespace ContaPlanner.Models
{
    [Flags]
    public enum TipoCuenta
    {
        [Description("")]
        Ninguno = 0,
        [Description("Cuentas corrientes")]
        CuentaCorriente = 1,
        [Description("Cuentas de ahorro")]
        CuentaAhorro = 2,
        [Description("Tarjetas de crédito")]
        TarjetaCredito = 4,
        [Description("Dinero en efectivo")]
        Efectivo = 8,
        [Description("Personas")]
        Persona = 16,
        [Description("Dinero en especie")]
        Especie = 32,
        [Description("Otras")]
        CuentaNormal = 64
    };

    [Table("Cuentas")]
    public class Cuenta
    {
        [Key]
        public int CuentaId { get; set; }
        [Required]
        public string Nombre { get; set; }
        public TipoCuenta Tipo { get; set; }
        public decimal SaldoInicial { get; set; }
        public bool Mostrar { get; set; }
        public override string ToString() { return Nombre; }

        public virtual Banco Banco { get; set; }
        public virtual Cuenta Asociada { get; set; }

        [InverseProperty("Destino")]
        public virtual List<Recibo> RecibosDestino { get; set; }
        [InverseProperty("Origen")]
        public virtual List<Recibo> RecibosOrigen { get; set; }

        [NotMapped]
        public string Categoria { get { return Tipo.Categoria(); } }

        [NotMapped]
        public string Detalles
        {
            get
            {
                switch (Tipo)
                {
                    case TipoCuenta.CuentaCorriente:
                    case TipoCuenta.CuentaAhorro:
                        return Banco != null ? String.Format("({0})", Banco.Nombre) : String.Empty;
                    case TipoCuenta.TarjetaCredito:
                        if (Asociada == null) return Asociada != null ? String.Format("({0})", Asociada.Nombre) : String.Empty;
                        return Asociada.Banco != null ? String.Format("({0}, {1})", Asociada.Nombre, Asociada.Banco.Nombre) : String.Format("({0})", Asociada.Nombre);
                }
                return String.Empty;
            }
        }

        [NotMapped]
        public decimal Saldo
        {
            get
            {
                using (var context = new ContaPlannerContext())
                {
                    var ingresos = context.Asientos.Include("Destino").Where(c => c.Destino.CuentaId == CuentaId).Sum(c => (decimal?)c.Importe) ?? 0M;
                    var gastos = context.Asientos.Include("Origen").Where(c => c.Origen.CuentaId == CuentaId).Sum(c => (decimal?)c.Importe) ?? 0M;
                    return SaldoInicial + ingresos - gastos;
                }
            }
        }

        [NotMapped]
        public bool SaldoEsNegativo { get { return Saldo < 0; } }

        public IEnumerable<Movimiento> GetMovimientosRecibos(DateTime desde, DateTime hasta)
        {
            var movimientos = new List<Movimiento>();
            using (var context = new ContaPlannerContext())
            {
                foreach (var recibo in context.Recibos
                    .Include("Origen")
                    .Include("Destino")
                    .Where(r => r.Origen.CuentaId == CuentaId || r.Destino.CuentaId == CuentaId))
                {
                    var apartir = desde;
                    while (apartir < hasta)
                    {
                        var movimiento = recibo.GetNextMovimiento(this, apartir);
                        if (movimiento.Fecha > hasta) break;
                        Debug.WriteLine(movimiento);
                        movimientos.Add(movimiento);
                        apartir = movimiento.Fecha.AddDays(1);
                    }
                }
            }
            return movimientos;
        }

        [NotMapped]
        public IEnumerable<Movimiento> Movimientos
        {
            get
            {
                var movimientos = new List<Movimiento>();
                var saldo = SaldoInicial;
                using (var context = new ContaPlannerContext())
                {
                    foreach (var asiento in context.Asientos
                        .Include("Origen").Include("UbicacionOrigen")
                        .Include("Destino").Include("UbicacionDestino")
                        .Include("Categoria").Include("Categoria.SuperCategoria")
                        .OrderBy(c => c.Fecha)
                        .Where(a => a.Origen.CuentaId == CuentaId || a.Destino.CuentaId == CuentaId))
                    {
                        if (asiento.Origen.CuentaId == CuentaId)
                        {
                            saldo = saldo - asiento.Importe;
                            movimientos.Add(new Movimiento
                            {
                                Fecha = asiento.Fecha,
                                OtraCuenta = asiento.Destino,
                                OtraUbicacion = asiento.UbicacionDestino,
                                ImporteMas = 0,
                                ImporteMenos = asiento.Importe,
                                Saldo = saldo,
                                Tipo = asiento.Tipo,
                                Categoria = asiento.Categoria
                            });
                        }
                        else if (asiento.Destino.CuentaId == CuentaId)
                        {
                            saldo = saldo + asiento.Importe;
                            movimientos.Add(new Movimiento
                            {
                                Fecha = asiento.Fecha,
                                OtraCuenta = asiento.Origen,
                                OtraUbicacion = asiento.UbicacionOrigen,
                                ImporteMas = asiento.Importe,
                                ImporteMenos = 0,
                                Saldo = saldo,
                                Tipo = asiento.Tipo,
                                Categoria = asiento.Categoria
                            });                            
                        }
                    }
                }
                return movimientos;//.OrderByDescending(m => m.Fecha);
            }
        }

        public Asiento LastAsiento(Cuenta origen, DateTime antes)
        {
            using (var context = new ContaPlannerContext())
            {
                var asientosantes = context.Asientos
                        .Include("Origen")
                        .Include("Destino")
                        .Where(a => a.Origen.CuentaId == origen.CuentaId && a.Destino.CuentaId == CuentaId)
                        .Where(a => a.Fecha <= antes);
                return asientosantes.FirstOrDefault(a => a.Fecha == asientosantes.Max(max => max.Fecha));
            }
        }

        public List<Asiento> LastAsientos(Cuenta origen, DateTime antes, int numero)
        {
            using (var context = new ContaPlannerContext())
            {
                var asientosantes = context.Asientos
                        .Include("Origen")
                        .Include("Destino")
                        .Where(a => a.Origen.CuentaId == origen.CuentaId && a.Destino.CuentaId == CuentaId)
                        .Where(a => a.Fecha <= antes);
                return asientosantes.OrderByDescending(a => a.Fecha).Take(numero).ToList();
            }
        }

    }
}
